package com.csx.base.concurrency.aqs.share;

import com.csx.base.concurrency.util.TimeUtil;

import java.util.concurrent.*;
import java.util.concurrent.atomic.AtomicIntegerArray;
import java.util.concurrent.atomic.AtomicReferenceArray;

/**
 * <p> what does this class do ? </p>
 *   这里是 CountDownLatch 的使用案例. 主要有两个使用场景
 *   1. 一等多:
 *      初始化 count = x,多个线程调用 countDown() 对 count 进行减一，
 *      一条线程 await() 阻塞，当 count = 0 时阻塞的线程开始执行
 *    场景构建： 我这里是设定了一个业务场景： 假设现在有多个读取任务分别有多个线程执行，主线程等待所有子线程读取完毕后再进行汇聚操作。
 *   2. 多等一:(使用场景不多)
 *      初始化 count = 1,多个线程调用 await() 阻塞等待，一条线程调用
 *      countDown() 唤醒所有阻塞的线程
 *    场景构建：我这里模拟的场景是多个线程同时开始执行性能测试场景，只有当多个性能测试线程初始化完毕后，主线程调用 countDown方法一声号令让所有子线程开始执行性能测试任务
 * <p> How does it work ?</p>
 * <p> Why does it do this ?</p>
 *
 * @author cuisongxu
 * @date 2024/12/4 周三 9:01
 */
public class CountDownLatchTest {

    private final ExecutorService executor = new ThreadPoolExecutor(
            5,
            10,
            300,
            TimeUnit.SECONDS,
            new ArrayBlockingQueue<>(100),
            new ThreadPoolExecutor.AbortPolicy()
    );

    // 一等多 场景下使用的 countDownLatch 对象
    private final CountDownLatch oneWaitMulti = new CountDownLatch(5);

    // 存储子线程的执行结果
    private final AtomicReferenceArray<String> subResArr = new AtomicReferenceArray<>(5);

    // 主线程汇聚所有子线程执行结果的结果
    private String res = "";


    // 多等一 场景下使用的 countDownLatch 对象
    private final CountDownLatch multiWaitOne = new CountDownLatch(1);

    private final AtomicIntegerArray initFlagArr = new AtomicIntegerArray(10);

    // 测试 一等多 场景的入口方法
    public void testOneWaitMulti() {
        // 将5个子任务提交给线程池执行
        for (int i = 0; i < 5; i++) {
            executor.submit(new SubTask(i));
        }
        try {
            // 等待所有子线程的读取任务执行完毕,主线程执行汇聚任务
            oneWaitMulti.await();
            for (int i = 0; i < subResArr.length(); i++) {
                if(subResArr.length() -1 != i) {
                    res = res + subResArr.get(i) + " + ";
                }else {
                    res = res + subResArr.get(i);
                }
            }

            System.out.println("主线程汇聚子线程的执行结果为: " + res);
        } catch (InterruptedException e) {
            throw new RuntimeException(e);
        }
    }

    // 测试 多等一 场景的入口方法
    public void testMultiWaitOne() {
        Thread[] threads = new Thread[10];
        // 启动10个线程开启性能测试
        for (int i = 0; i < 10; i++) {
            Thread thread = new Thread(new PerformanceTestTask(i), "thread" + i);
            thread.start();
            threads[i] = thread;

        }

        // 等待所有线程初始化完毕
        while(!initAllSuccess()) {
            try {
                TimeUnit.MILLISECONDS.sleep(100);
            } catch (InterruptedException e) {
                throw new RuntimeException(e);
            }
        }

        // 同一时刻让所有性能测试线程开启性能测试
        multiWaitOne.countDown();

        // 等待所有线程执行完毕
        for (int i = 0; i < threads.length; i++) {
            try {
                threads[i].join();
            } catch (InterruptedException e) {
                throw new RuntimeException(e);
            }
        }

        System.out.println("main end");
    }

    class SubTask implements Runnable {

        // 需要读取的页
        private final int page;

        public SubTask(int page) {
            this.page = page;
        }

        @Override
        public void run() {
            try {
                // 模拟读取该页的耗时
                TimeUnit.SECONDS.sleep(1);
                subResArr.set(page, "读取 " + page + " 的结果集");
                String nowTimeStr = TimeUtil.getNowTimeStr();
                System.out.println(nowTimeStr + " " + Thread.currentThread().getName() + "读取的子结果集: " + subResArr.get(page));
                // 调用countDown方法使得count-1.表示该子线程执行任务完毕
                oneWaitMulti.countDown();
            } catch (InterruptedException e) {
                throw new RuntimeException(e);
            }
        }
    }

    private boolean initAllSuccess() {
        for (int i = 0; i < initFlagArr.length(); i++) {
            if(initFlagArr.get(i) != 1) {
                return false;
            }
        }
        return true;
    }

    class PerformanceTestTask implements Runnable {

        private final int initFlagIndex;

        public PerformanceTestTask(int initFlag) {
            this.initFlagIndex = initFlag;
        }

        @Override
        public void run() {

            String nowTimeStr;
            try {
                // 随机休眠 1000 - 4000 毫秒模拟初始化工作的延时
                nowTimeStr = TimeUtil.getNowTimeStr();
                System.out.println(nowTimeStr + " " + Thread.currentThread().getName() + " 执行性能测试前的初始化工作");
                TimeUnit.MILLISECONDS.sleep((long) (Math.random() * 3000) + 1000);
                // 将初始化标识修改为true，表示性能测试前的初始化工作完毕
                initFlagArr.set(initFlagIndex, 1);

                // 在此等待主线程发号施令，多个线程一起开始执行性能测试工作
                multiWaitOne.await();
                nowTimeStr = TimeUtil.getNowTimeStr();
                System.out.println(nowTimeStr + " " + Thread.currentThread().getName() + "开始执行性能测试工作");

                // 模拟性能测试耗时
                TimeUnit.MILLISECONDS.sleep((long) (Math.random() * 4000) + 2000);
                nowTimeStr = TimeUtil.getNowTimeStr();
                System.out.println(nowTimeStr + " " + Thread.currentThread().getName() + "性能测试工作完毕");

            } catch (InterruptedException e) {
                throw new RuntimeException(e);
            }
        }
    }


    public static void main(String[] args) {
        CountDownLatchTest countDownLatchTest = new CountDownLatchTest();
//        countDownLatchTest.testOneWaitMulti();
        countDownLatchTest.testMultiWaitOne();
    }
}
